package MyGui;

import java.awt.*;
import java.awt.event.*;
import java.io.*;
import javax.swing.*;
import javax.swing.table.*;
import java.text.*;
import java.util.*;

import DistSys.*;
import Element.*;
import Element.Geom.*;


/**
 * The class <code>TxParaDlg</code> implements the cost summary dialog.
 *
 */
public class TxParaDlg extends MyDlgFrame implements ActionListener,
                                                     WindowListener {
    /* data indices */
    private static final int BASE_RATING       = 0;
    private static final int PRIMARY_VOLTAGE   = 1;
    private static final int SECONDARY_VOLTAGE = 2;
    private static final int IMPEDANCE         = 3;
    private static final int NO_LOAD_LOSSES    = 4;
    private static final int X_R               = 5;
    private static final int Y_G               = 6;
    private static final int DATA_COUNT        = 7;
    private final MessageFormat descFormat     = new MessageFormat(
        "Base Rating = {" + BASE_RATING + ",number} MVA, " +
        "{" + PRIMARY_VOLTAGE + ",number}:{" + SECONDARY_VOLTAGE + ",number} kV, " +
        "Z={" + IMPEDANCE + ",number}%, X/R={" + X_R + ",number}, " +
        "NLL={" + NO_LOAD_LOSSES + ",number}%, Y/G={" + Y_G + ",number}, " +
        "{" + DATA_COUNT + "}"); // previous description

    /* result indices */
    private static final int RATIO        = 0;
    private static final int RESISTANCE   = 1;
    private static final int REACTANCE    = 2;
    private static final int CONDUCTANCE  = 3;
    private static final int SUCEPTANCE   = 4;
    private static final int PRECISION_CHANGE = 3;
    private static final int RESULT_COUNT     = 5;

    /* Dialog items */
    private double[] adData;
    private JTextField[] atfData;
    private double[] adResult;
    private JTextField[] atfResult;
    private JCheckBox checkChangeDesc;
    private JButton btnShowHide;
    private JButton btnApply;
    private ElemListScrollPane scrollTx;

    /* Show/hide list */
    private int iIsShown = 0; // list is initially hidden
    private int[] aiHeight = new int[] { 388, 580 }; // initial heights
    private String[] asBtnText = new String[] { "Show List", "Hide List" };
    private Component[] aNextFocusComp = new Component[] { null, null };

    /* Block action on activate window */
    private int iBlockActivated = 0;

    /**
      * Creates a <code>TxParaDlg</code>.
      */
    public TxParaDlg(MainFrame parent, String title) {
        super(parent, title);
        setSize(260, aiHeight[iIsShown]);
        setResizable(iIsShown != 0);
        Container contentPane = getContentPane();
        contentPane.setLayout(gridBagLayout);

        // Initialize data
        adData = new double[] {
             15.0,   // BASE_RATING
            115.0,   // PRIMARY_VOLTAGE
             12.47,  // SECONDARY_VOLTAGE
              4.0,   // IMPEDANCE
              0.25,  // NO_LOAD_LOSSES
              9.0,   // X_R
              1.0,   // Y_G
        };
        adResult = new double[RESULT_COUNT];

        // Initialize buttons
        btnShowHide = new JButton(asBtnText[iIsShown]);
        btnShowHide.setMnemonic('L');
        btnShowHide.addActionListener(this);
        btnApply = new JButton("Apply");
        btnApply.setMnemonic('A');
        btnApply.addActionListener(this);
        checkChangeDesc = new JCheckBox("Place Data in Description");
        checkChangeDesc.setMnemonic('D');
        checkChangeDesc.setSelected(true);

        // Create panels
        GridBagConstraints c = new GridBagConstraints();
        c.fill = GridBagConstraints.BOTH; c.anchor = GridBagConstraints.WEST;
        c.insets = new Insets(0, 0, 0, 0);

        JPanel datapanel = new JPanel(gridBagLayout);
        c.gridx = 0; c.gridy = 0; c.gridwidth = 2; c.weightx = 1;
        contentPane.add(datapanel, c);

        JPanel resultpanel = new JPanel(gridBagLayout);
        c.gridy = 1; c.insets.top = GAP; contentPane.add(resultpanel, c);

        c.fill = GridBagConstraints.NONE; c.gridwidth = 1; c.gridy = 2;
        c.insets.left = c.insets.right = GAP;
        contentPane.add(btnApply, c);
        c.anchor = GridBagConstraints.EAST; c.gridx = 1;
        contentPane.add(btnShowHide, c);

        JPanel applypanel = new JPanel(gridBagLayout);
        c.gridx = 0; c.gridwidth = 2; c.gridy = 3; c.weighty = 1;
        c.fill = GridBagConstraints.BOTH; c.anchor = GridBagConstraints.WEST;
        c.insets.left = c.insets.right = 0;
        contentPane.add(applypanel, c);

        // Construct data panel
        datapanel.setBorder(BorderFactory.createTitledBorder(
            BorderFactory.createEtchedBorder(), " Data "));
        atfData = new JTextField[DATA_COUNT];
        final String[] asDataLabels = new String[] {
            "Base rating (MVA)",
            "Primary Voltage (kV)",
            "Secondary Voltage (kV)",
            "Impedance (%)",
            "No Load Losses (%)",
            "X/R",
            "Y/G"
        };

        c.weighty = 0; c.gridwidth = 1; c.anchor = GridBagConstraints.WEST;
        c.insets.top = c.insets.bottom = 0;c.insets.left = c.insets.right = GAP;
        for (int i = 0; i < DATA_COUNT; i++) {
            atfData[i] = new JTextField(""+adData[i], 4);
            atfData[i].setInputVerifier(ivDataVerifier);
            JLabel labelData = new JLabel(asDataLabels[i]);
            labelData.setDisplayedMnemonic(asDataLabels[i].charAt(0));
            labelData.setLabelFor(atfData[i]);

            c.gridy = i;
            if (i == DATA_COUNT-1)
                c.insets.bottom = GAP;
            c.gridx = 0; c.weightx = 1; c.insets.right = 0;
            datapanel.add(atfData[i], c);
            c.gridx = 1; c.weightx = 0; c.insets.right = GAP;
            datapanel.add(labelData, c);
        }
        atfData[DATA_COUNT-1].setNextFocusableComponent(checkChangeDesc);

        // Construct resultpanel
        resultpanel.setBorder(BorderFactory.createTitledBorder(
            BorderFactory.createEtchedBorder(), " Results "));
        atfResult = new JTextField[RESULT_COUNT];
        final String[] asResultLabels = new String[] {
            "Winding Ratio",
            "Winding Resistance (Ohms)",
            "Winding Reactance (Ohms)",
            "Shunt Conductance (Mhos)",
            "Shunt Suceptance (Mhos)",
        };

        c.anchor = GridBagConstraints.NORTHWEST; c.insets.bottom = 0;
        for (int i = 0; i < RESULT_COUNT; i++) {
            atfResult[i] = new JTextField(""+adResult[i], 6);
            atfResult[i].setBorder(null);
            atfResult[i].setEditable(false);
            atfResult[i].setForeground(Color.blue);
            atfResult[i].setBackground(getBackground());
            atfResult[i].setHorizontalAlignment(JTextField.RIGHT);
            JLabel labelResult = new JLabel(asResultLabels[i]);

            c.gridy = i;
            c.gridx = 0; c.weightx = 0; c.insets.right = 0;
            resultpanel.add(labelResult, c);
            c.gridx = 1; c.weightx = 1; c.insets.right = GAP;
            resultpanel.add(atfResult[i], c);
        }
        c.gridx = 0; c.gridy = RESULT_COUNT; c.gridwidth = 2; c.weighty = 1;
        resultpanel.add(checkChangeDesc, c);

        // Construct applypanel
        applypanel.setBorder(BorderFactory.createTitledBorder(
            BorderFactory.createEtchedBorder(), " Apply to "));
        scrollTx = new ElemListScrollPane(mainFrame,
            ElemListTableModel.PANE_TXPARA, JTable.AUTO_RESIZE_OFF);

        c.weighty = 1; c.fill = GridBagConstraints.BOTH; c.insets.bottom = GAP;
        c.gridx = 0; c.gridy = 0; applypanel.add(scrollTx, c);

        calculate();

        aNextFocusComp[0] = atfData[0];
        aNextFocusComp[1] = scrollTx;
        btnShowHide.setNextFocusableComponent(aNextFocusComp[iIsShown]);

        addWindowListener(this);
    }

    public void windowOpened( WindowEvent evt ){ }
    public void windowClosed( WindowEvent evt ){ }
    public void windowIconified( WindowEvent evt ){ }
    public void windowDeiconified( WindowEvent evt ){ }
    public void windowActivated( WindowEvent evt ){
        if (iBlockActivated > 0)
            // We are blocked at least once. Decrease block counter
            iBlockActivated--;
        else {
            Vector data = scrollTx.getData();
            data.removeAllElements();
            // Add all transformers
            for (int i = 0; i < Sys.elemList.size(); i++) {
                AbsElem elem = (AbsElem)Sys.elemList.elementAt(i);
                int iType = elem.getType();
                if (iType == SysConst.TYPE_TRANSFORMER ||
                    iType == SysConst.TYPE_REGULATOR ||
                    iType == SysConst.TYPE_3W_TX) {
                    data.add(elem);
                }
            }
            scrollTx.getModel().fireTableDataChanged();
            // Select all selected transformers
            JTable table = scrollTx.getTable();
            for (int i = 0; i < data.size(); i++) {
                if (((AbsElem)data.elementAt(i)).getElemGeomObj().isSelected) {
                    table.addRowSelectionInterval(i, i);
                }
            }
            if (table.getSelectedRowCount() == 1) {
                // If only one transformer is selected, try to get its data
                AbsElem elem = (AbsElem)data.elementAt(table.getSelectedRow());
                String sDesc = elem.getElemGeomObj().getDesc();
                Object[] aobjData = descFormat.parse(sDesc,
                    new ParsePosition(0));
                if (aobjData != null) {
                    // Data retrieval successful
                    for (int i = 0; i < DATA_COUNT; i++) {
                        adData[i] = ((Number)aobjData[i]).doubleValue();
                        atfData[i].setText(""+adData[i]);
                    }
                }
            }
        }
    }
    public void windowDeactivated( WindowEvent evt ){ }
    public void windowClosing( WindowEvent evt ){ }

    /*
     * This input verifier verifies the data as well as
     * recalculates the results based on the new data value.
     */
    private InputVerifier ivDataVerifier = new InputVerifier() {
        public boolean verify(JComponent input) {
            try {
                for (int i = 0; i < DATA_COUNT; i++) {
                    if (input == atfData[i]) {
                        double d = Double.parseDouble(atfData[i].getText());
                        if (d != adData[i]) {
                            adData[i] = d;
                            calculate();
                        }
                        return true;
                    }
                }
            } catch(NumberFormatException e) {
                iBlockActivated++; // Increase block counter
                JOptionPane.showMessageDialog(input, "Please input a number!");
            }
            return false;
        }
    };

    /**
     * This method is public as an implementation side effect.
     * do not call or override.
     */
    public void actionPerformed(ActionEvent evt) {
        if (evt.getSource() == btnApply) {
            Object[] aobjData = null;
            ParsePosition pp = null;
            if (checkChangeDesc.isSelected()) {
                // If we shall place data in description, prepare data
                aobjData = new Object[DATA_COUNT+1];
                for (int i = 0; i < DATA_COUNT; i++) {
                    aobjData[i] = new Double(adData[i]);
                }
                pp = new ParsePosition(0);
            }

            int iCount = 0;
            Sys.clearSelectionAndFocus();
            int[] aiRows = scrollTx.getTable().getSelectedRows();
            for (int i = 0; i < aiRows.length; i++) {
                AbsElem elem = (AbsElem)scrollTx.getData().elementAt(aiRows[i]);
                // Add to selection
                Sys.addElemToSelection(elem);
                // Change data
                elem.setData(AbsElem.DATA_TXFM_RATIO,  adResult[RATIO]);
                elem.setData(AbsElem.DATA_RESISTANCE,  adResult[RESISTANCE]);
                elem.setData(AbsElem.DATA_REACTANCE,   adResult[REACTANCE]);
                elem.setData(AbsElem.DATA_SHUNT_COND,  adResult[CONDUCTANCE]);
                elem.setData(AbsElem.DATA_SHUNT_SUCEP, adResult[SUCEPTANCE]);
                elem.setFlag(AbsElem.DATA_TXFM_RATIO,  1);
                elem.setFlag(AbsElem.DATA_RESISTANCE,  1);
                elem.setFlag(AbsElem.DATA_REACTANCE,   1);
                elem.setFlag(AbsElem.DATA_SHUNT_COND,  1);
                elem.setFlag(AbsElem.DATA_SHUNT_SUCEP, 1);
                if (aobjData != null) {
                    // Change description
                    AbsElemGeom geom = elem.getElemGeomObj();
                    pp.setIndex(0);
                    // Get previous data
                    Object[] aobjPrevData = descFormat.parse(geom.getDesc(),pp);
                    if (aobjPrevData != null) {
                        // Copy description (last part of the data)
                        aobjData[DATA_COUNT] = aobjPrevData[DATA_COUNT];
                    } else {
                        // The description obviously didn't contain data,
                        // so copy whole description
                        aobjData[DATA_COUNT] = geom.getDesc();
                    }
                    geom.setDesc(descFormat.format(aobjData));
                }

                // Increase counter
                iCount++;
            }
            mainFrame.mainPanel.setNeedRedraw(true);
            mainFrame.mainPanel.repaint();
            iBlockActivated++; // Increase block counter
            JOptionPane.showMessageDialog(this, "" + iCount + " component" +
                (iCount != 1 ? "s" : "") + " adjusted.");
        } else { // if (evt.getSource() == btnShowHide)
            toggleShowHide();
        }
    }

    /*
     * Toggles the size of the dialog and adjusts everything.
     */
    private void toggleShowHide() {
        // Save old height
        Dimension d = getSize();
        aiHeight[iIsShown] = d.height;
        // Toggle iIsShown
        iIsShown = 1 - iIsShown;
        // Set new heights, button text and next focusable component
        setSize(d.width, aiHeight[iIsShown]);
        setResizable(iIsShown != 0);
        btnShowHide.setText(asBtnText[iIsShown]);
        btnShowHide.setNextFocusableComponent(aNextFocusComp[iIsShown]);
        // Validate layout
        validate();
    }

    /*
     * Calculates the results based on the data and shows it.
     */
    private void calculate() {
        double dZbase = adData[PRIMARY_VOLTAGE] * adData[PRIMARY_VOLTAGE] /
            adData[BASE_RATING];

        adResult[RATIO] = adData[SECONDARY_VOLTAGE] / adData[PRIMARY_VOLTAGE];
        adResult[CONDUCTANCE] = adData[NO_LOAD_LOSSES] / (100 * dZbase);
        adResult[SUCEPTANCE] = adData[Y_G] * adResult[CONDUCTANCE];

        double dZ = dZbase * adData[IMPEDANCE] / 100;
        adResult[RESISTANCE] = dZ / Math.sqrt(1 + adData[X_R] * adData[X_R]);
        adResult[REACTANCE] = adData[X_R] * adResult[RESISTANCE];

        NumberFormat nf = NumberFormat.getNumberInstance();
        nf.setMinimumFractionDigits(3);
        nf.setMaximumFractionDigits(3);
        for (int i = 0; i < RESULT_COUNT; i++) {
            if (i == PRECISION_CHANGE) {
                nf.setMinimumFractionDigits(6);
                nf.setMaximumFractionDigits(6);
            }
            atfResult[i].setText(nf.format(adResult[i]));
            atfResult[i].setCaretPosition(0);
        }
    }
}
